﻿/*
 *       This file is part of VFC.
 *
 *        VFC is free software: you can redistribute it and/or modify
 *        it under the terms of the GNU General Public License as published by
 *        the Free Software Foundation, either version 3 of the License, or
 *        (at your option) any later version.
 *
 *        VFC is distributed in the hope that it will be useful,
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *        GNU General Public License for more details.
 *
 *        You should have received a copy of the GNU General Public License
 *        along with VFC.  If not, see <http://www.gnu.org/licenses/>.
 */


using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;

namespace VFC.Nes.Cpu.Opcode
{
    /// <summary>
    /// Parent Class for the Addressing Mode...
    /// 
    /// Absolute Addressing Mode
    /// </summary>
    class _Absolute_
    {
        public _Absolute_()
        {
            //do nothng as of now...
        }

        protected static int GetAddress()
        {
            return (Cpu.Core.Param2 << 8) | Cpu.Core.Param1;
        }
    }

    /// <summary>
    /// AbsoluteX Addressing Mode
    /// </summary>
    class _AbsoluteX_
    {
        private static int NewAddr = 0, OldAddr = 0;

        public _AbsoluteX_()
        {
            //do nothing....
        }

        protected static int GetAddress()
        {
            OldAddr = (Cpu.Core.Param2 << 8) | Cpu.Core.Param1;
            NewAddr = OldAddr + Cpu.Register.X;
            return NewAddr & 0xffff;
        }

        protected static int GetAddressNoPageCheck()
        {
            return (((Cpu.Core.Param2 << 8) | Cpu.Core.Param1) + Cpu.Register.X) & 0xffff;
        }

        protected static void CheckPage()
        {
            if (Cpu.Memory.GetMemoryPage(NewAddr) != Cpu.Memory.GetMemoryPage(OldAddr))
            {
                Cpu.Core.Cycle += 1;
            }
        }
    }

    /// <summary>
    /// AbsoluteY Addressing Mode
    /// </summary>
    class _AbsoluteY_
    {
        private static int NewAddr = 0, OldAddr = 0;

        public _AbsoluteY_()
        {
            //do nothing...
        }

        protected static int GetAddress()
        {
            OldAddr = (Cpu.Core.Param2 << 8) | Cpu.Core.Param1;
            NewAddr = OldAddr + Cpu.Register.Y;
            return NewAddr & 0xffff;
        }

        protected static int GetAddressNoPageCheck()
        {
            return (((Cpu.Core.Param2 << 8) | Cpu.Core.Param1) + Cpu.Register.Y) & 0xffff;
        }

        protected static void CheckPage()
        {
            if (Cpu.Memory.GetMemoryPage(NewAddr) != Cpu.Memory.GetMemoryPage(OldAddr))
            {
                Cpu.Core.Cycle += 1;
            }
        }
    }

    /// <summary>
    /// IndirectX Addressing Mode
    /// </summary>
    class _IndirectX_
    {
        private static int MSB = 0, LSB = 0, Addr = 0;

        public _IndirectX_()
        {
            //do nothing...
        }

        protected static int GetAddress()
        {
            LSB = Cpu.Memory.Read((Cpu.Core.Param1 + Cpu.Register.X) & 0xff);
            MSB = Cpu.Memory.Read(((Cpu.Core.Param1 + Cpu.Register.X) + 1) & 0xff);
            Addr = MSB * 0x100 + LSB;
            return Addr;
        }
    }

    /// <summary>
    /// IndirectY Addressing Mode
    /// </summary>
    class _IndirectY_
    {
        private static int MSB = 0, LSB = 0, OldAddr = 0, NewAddr = 0;

        public _IndirectY_()
        {
            //do nothing...
        }

        protected static int GetAddress()
        {
            LSB = Cpu.Memory.Read(Cpu.Core.Param1);
            MSB = Cpu.Memory.Read((Cpu.Core.Param1 + 1) & 0xff);
            OldAddr = (MSB << 8) | LSB;
            NewAddr = (OldAddr + Cpu.Register.Y) & 0xffff;
            return NewAddr;
        }

        protected static void CheckPage()
        {
            if (Cpu.Memory.GetMemoryPage(NewAddr) != Cpu.Memory.GetMemoryPage(OldAddr))
            {
                Cpu.Core.Cycle += 1;
            } 
        }
    }

    /// <summary>
    /// ZeroPageX Addressing Mode
    /// </summary>
    class _ZeroPageX_
    {
        public _ZeroPageX_()
        {
            //do nothing...
        }

        protected static int GetAddress()
        {
            return (Cpu.Core.Param1 + Cpu.Register.X) & 0xff;
        }
    }

    /// <summary>
    /// ZeroPageY Addressing Mode
    /// </summary>
    class _ZeroPageY_
    {
        public _ZeroPageY_()
        {
            //do nothing...
        }

        protected static int GetAddress()
        {
            return (Cpu.Core.Param1 + Cpu.Register.Y) & 0xff;
        }
    }

    /// <summary>
    /// Relative Addressing Mode - Branching
    /// </summary>
    class _Relative_
    {
        private static int tmp = 0;

        public _Relative_()
        {
            //Do nothing..
        }

        protected static void Branch()
        {
            /*
             *    Branch backward if opcode parameter is negative else
             *    if positive, branch forward...
             */
            Cpu.Register.PC = (Cpu.Register.PC + 1) & 0xffff;
            tmp = Cpu.Memory.Read(Cpu.Register.PC);
            if ((tmp & 0x80) == 0x80) //Check if the 8 bit is set.....
            {
                //If 8 bit is set (which means its a negative value),
                //branch backward...
                Cpu.Register.PC = (Cpu.Register.PC - (0xff - tmp)) & 0xffff;
            }
            else
            {
                //If not, then branch forward...
                Cpu.Register.PC = (Cpu.Register.PC + tmp + 1) & 0xffff;
            }
        }
    }
}
